<template>
  <div class="cov-vue-date" :class="option.wrapperClass ? option.wrapperClass : {}">
    <div
      @mouseenter="showClear"
      @mouseleave="hideClear"
      class="relative">
      <input
        v-if="showInput"
        type="text"
        title="输入日期"
        class="cov-datepicker"
        readonly="readonly"
        v-model="time"
        :placeholder="defaultplace"
        @click.stop="showCheck"
        :style="{background: background, borderColor: borderColor}"
        :class="inputClass" />
      <i class="iconfont icon-date" v-if="isIcon"></i>
      <div v-if="ClearTipShow">
        <div class="icon-talk-x" v-tooltip="'清空'" @click="clear" v-if="isClear" :style="{top: `${clearPosition.top}px`, right: `${clearPosition.right}px`}">
          <i class="iconfont icon-x"></i>
        </div>
      </div>
    </div>
    <div
      class="datepicker-overlay"
      v-if="showInfo.check"
      @click="dismiss($event)"
      :style="{'background' : option.overlayOpacity? 'rgba(0,0,0,'+option.overlayOpacity+')' : 'rgba(0,0,0,0.5)'}">
      <div class="cov-date-body cov-date-body-fixed">
        <div class="cov-date-head clear">
          <p class="title left">选择时间</p>
          <i class="iconfont icon-x right" @click.stop='close'></i>
        </div>

        <div class="mlr20">
          <!--类型是年月的情况下-->
          <div class="cov-date-monthly clear" v-if="type === 'year'">
            <div class="cov-date-caption" style="width: 100%">
              <b @click="showYear">{{checked.year}}.</b>
              <b @click="showMonth">{{displayInfo.month}}</b>
            </div>
          </div>

          <!--类型是年月日的情况下-->
          <div class="cov-date-monthly clear" v-else>
            <div class="cov-date-previous" @click="nextMonth('pre')">
              <i class="iconfont icon-t-arrow"></i>
            </div>
            <div class="cov-date-caption">
              <b @click="showYear">{{checked.year}}.</b>
              <b @click="showMonth">{{displayInfo.month}}</b>
            </div>
            <div class="cov-date-next" @click="nextMonth('next')">
              <i class="iconfont icon-r-arrow"></i>
            </div>
          </div>

          <div class="cov-date-box" v-if="showInfo.day">
            <div class="cov-picker-box">
              <div class="week">
                <ul>
                  <li v-for="weekie in library.week">{{weekie}}</li>
                </ul>
              </div>
              <div class="day"
                   v-for="day in dayList"
                   track-by="$index"
                   @click.stop="checkDay(day)">
                <p
                  :class="{'checked':day.checked,'unavailable':day.unavailable,'passive-day': !(day.inMonth)}"
                  style="width: 100%;"
                  :style="day.checked ? (option.color && option.color.checkedDay ? { background: option.color.checkedDay } : { background: '#edfaf7' }) : {}"
                >{{day.value}}</p>
              </div>
            </div>
          </div>

          <div class="cov-date-box list-box" v-if="showInfo.year">
            <div class="cov-picker-box date-list" id="yearList">
              <div class="date-item" v-for="yearItem in library.year" track-by="$index" @click="setYear(yearItem)">{{yearItem}}</div>
            </div>
          </div>

          <div class="cov-date-box list-box" v-if="showInfo.month">
            <div class="cov-picker-box date-list">
              <div class="date-item" v-for="monthItem in library.month" track-by="$index" @click="setMonth(monthItem)">{{monthItem}}</div>
            </div>
          </div>

          <div class="cov-date-box list-box" v-if="showInfo.hour">
            <div class="cov-picker-box date-list">
              <div class="watch-box">
                <div class="hour-box">
                  <div class="mui-pciker-rule mui-pciker-rule-ft"></div>
                  <ul>
                    <li class="hour-item" v-for="hitem in hours" @click="setTime('hour', hitem, hours)" :class="{'active':hitem.checked}">{{hitem.value}}</li>
                  </ul>
                </div>
                <div class="min-box" v-if="type === 'min'">
                  <div class="mui-pciker-rule mui-pciker-rule-ft"></div>
                  <div class="min-item" v-for="mitem in mins" @click="setTime('min',mitem, mins)" :class="{'active':mitem.checked}">{{mitem.value}}</div>
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="button-box cov-date-foot">
          <span class="mr10 pointer" @click="doThisYear" v-if="showThisYear">全年</span>
          <span class="mr10 pointer" @click="checkToday" v-if="isToday">今天</span>
          <span class="color" @click="picked" v-if="hideConfirm">确定</span>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  export default {
    name: 'zz-date',
    props: {
      // 是否能修改时间
      disabled: {
        type: Boolean,
        default: false
      },
      // 是否显示输入框
      showInput: {
        type: Boolean,
        default: true
      },
      // 改变这个值显示这个选择时间插件
      showWeek: {
        type: Number,
        default: 0
      },
      disabledTip: {
        type: String,
        default: ''
      },
      background: {
        type: String,
        default: ''
      },
      borderColor: {
        type: String,
        default: ''
      },
      option: {
        type: Object,
        default () {
          return {
            SundayFirst: false,
            week: ['一', '二', '三', '四', '五', '六', '日'],
            month: ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'],
            format: 'YYYY-MM-DD HH:mm',
            wrapperClass: '',
            overlayOpacity: 0.5,
            dismissible: true
          }
        }
      },
      inputClass: {
        type: String,
        default: 'date-input'
      },
      // 时间类型
      type: {
        type: String,
        default: 'min'
      },
      place: {
        type: String,
        default: '选择时间'
      },
      // 重置时间
      resetPlace: {
        type: Number,
        default: 0
      },
      limit: {
        type: Array,
        default () {
          return []
        }
      },
      dateSouce: [Object, String, Number],
      // 显示全年按钮
      showThisYear: {
        type: Boolean,
        default: false
      },
      // 隐藏确定 按钮
      hideConfirm: {
        type: Boolean,
        default: true
      },
      isToday: {
        type: Boolean,
        default: true
      },
      // 显示隐藏清空icon，不显示不可清空（时间必选时用）
      ClearTipShow: {
        type: Boolean,
        default: true
      },
      isIcon: {
        type: Boolean,
        default: false
      },
      clearPosition: {
        type: Object,
        default: function () {
          return {
            top: 8,
            right: 10
          }
        }
      }
    },
    watch: {
      place (val) {
        if (!val) {
          this.defaultplace = '选择时间'
        } else {
          if (val.split('-').length > 2) {
            this.time = val
          } else {
            this.time = ''
          }
          this.defaultplace = val
        }
      },
      resetPlace () {
        this.time = ''
        this.defaultplace = '选择时间'
        this.remove()
      },
      showWeek () {
        this.showCheck()
      }
    },
    data () {
      function hours () {
        let list = []
        let hour = 24
        while (hour > 0) {
          hour--
          list.push({
            checked: false,
            value: hour < 10 ? '0' + hour : hour
          })
        }
        return list
      }
      function mins () {
        let list = []
        let min = 60
        while (min > 0) {
          min--
          list.push({
            checked: false,
            value: min < 10 ? '0' + min : min
          })
        }
        return list
      }
      return {
        hours: hours(),
        mins: mins(),
        defaultplace: '选择时间',
        isClear: false,
        showInfo: {
          hour: false,
          day: false,
          month: false,
          year: false,
          check: false
        },
        displayInfo: {
          month: ''
        },
        library: {
          week: this.option.week,
          month: this.option.month,
          year: []
        },
        checked: {
          oldtime: '',
          currentMoment: null,
          year: '',
          month: '',
          day: '',
          hour: '00',
          min: '00'
        },
        dayList: [],
        time: '',
        selectedDays: []
      }
    },
    created () {
      this.defaultplace = this.place
      if (this.place.split('-').length > 2) {
        this.time = this.place
      }
    },
    methods: {
      pad (n) {
        n = Math.floor(n)
        return n < 10 ? '0' + n : n
      },
      nextMonth (type) {
        let next = null
        type === 'next' ? next = this.$moment(this.checked.currentMoment).add(1, 'M') : next = this.$moment(this.checked.currentMoment).add(-1, 'M')
        this.showDay(next)
      },
      showDay (time) {
        if (time === undefined) {
          this.checked.currentMoment = this.$moment()
        } else {
          this.checked.currentMoment = this.$moment(time, this.option.format)
        }
        this.showOne('day')
        this.checked.year = this.$moment(this.checked.currentMoment).format('YYYY')
        this.checked.month = this.$moment(this.checked.currentMoment).format('MM')
        this.checked.day = this.$moment(this.checked.currentMoment).format('DD')
        this.displayInfo.month = this.library.month[this.$moment(this.checked.currentMoment).month()]
        let days = []
        let currentMoment = this.checked.currentMoment
        let firstDay = this.$moment(currentMoment).date(1).day()
        let previousMonth = this.$moment(currentMoment)
        let nextMonth = this.$moment(currentMoment)
        nextMonth.add(1, 'months')
        previousMonth.subtract(1, 'months')
        let monthDays = this.$moment(currentMoment).daysInMonth()
        let oldtime = this.checked.oldtime
        for (let i = 1; i <= monthDays; ++i) {
          days.push({
            value: i,
            inMonth: true,
            unavailable: false,
            checked: false,
            moment: this.$moment(currentMoment).date(i)
          })
          if (i === Math.ceil(this.$moment(currentMoment).format('D')) && this.$moment(oldtime, this.option.format).year() === this.$moment(currentMoment).year() && this.$moment(oldtime, this.option.format).month() === this.$moment(currentMoment).month()) {
            days[i - 1].checked = true
          }
          this.checkBySelectDays(i, days)
        }

        if (firstDay === 0) firstDay = 7
        for (let i = 0; i < firstDay - (this.option.SundayFirst ? 0 : 1); i++) {
          let passiveDay = {
            value: previousMonth.daysInMonth() - (i),
            inMonth: false,
            action: 'previous',
            unavailable: false,
            checked: false,
            moment: this.$moment(currentMoment).date(1).subtract(i + 1, 'days')
          }
          days.unshift(passiveDay)
        }

        let passiveDaysAtFinal = 42 - days.length
        for (let i = 1; i <= passiveDaysAtFinal; i++) {
          let passiveDay = {
            value: i,
            inMonth: false,
            action: 'next',
            unavailable: false,
            checked: false,
            moment: this.$moment(currentMoment).add(1, 'months').date(i)
          }
          days.push(passiveDay)
        }

        if (this.limit.length > 0) {
          for (let li of this.limit) {
            switch (li.type) {
              case 'fromto':
                days = this.limitFromTo(li, days)
                break
              case 'weekday':
                days = this.limitWeekDay(li, days)
                break
            }
          }
        }

        this.dayList = days
      },
      checkBySelectDays (d, days) {
        this.selectedDays.forEach(day => {
          if (this.checked.year === this.$moment(day).format('YYYY') && this.checked.month === this.$moment(day).format('MM') && d === Math.ceil(this.$moment(day).format('D'))) {
            days[d - 1].checked = true
          }
        })
      },
      limitWeekDay (limit, days) {
        days.map((day) => {
          if (limit.available.indexOf(Math.floor(day.this.$moment.format('d'))) === -1) {
            day.unavailable = true
          }
        })
        return days
      },
      limitFromTo (limit, days) {
        if (limit.from || limit.to) {
          days.map((day) => {
            if (this.getLimitCondition(limit, day)) {
              day.unavailable = true
            }
          })
        }
        return days
      },
      getLimitCondition (limit, day) {
        let month = ''
        let year = this.checked.year
        if (day.inMonth) {
          month = this.checked.month
        } else {
          if (day.action === 'previous') {
            month = parseInt(this.checked.month) - 1
          } else if (day.action === 'next') {
            if (this.checked.month === '12') {
              month = 1
              year = Number.parseInt(this.checked.year) + 1
            } else {
              month = parseInt(this.checked.month) + 1
            }
          }
        }
        let tmpMoment = this.$moment(year + '-' + this.pad(month) + '-' + this.pad(day.value))
        if (limit.from && !limit.to) {
          return !tmpMoment.isAfter(limit.from)
        } else if (!limit.from && limit.to) {
          return !tmpMoment.isBefore(limit.to)
        } else {
          return !tmpMoment.isBetween(limit.from, limit.to)
        }
      },
      checkDay (obj) {
        if (obj.unavailable || obj.value === '') {
          return false
        }
        if (!(obj.inMonth)) {
          this.nextMonth(obj.action)
        }
        if (this.type === 'day' || this.type === 'min') {
          this.dayList.forEach((x) => {
            x.checked = false
          })
          this.checked.day = this.pad(obj.value)
          obj.checked = true
        } else {
          let day = this.pad(obj.value)
          let ctime = this.checked.year + '-' + this.checked.month + '-' + day
          if (obj.checked === true) {
            obj.checked = false
            this.selectedDays.$remove(ctime)
          } else {
            this.selectedDays.push(ctime)
            obj.checked = true
          }
        }
        switch (this.type) {
          case 'day':
            this.picked()
            break
          case 'min':
            this.showOne('hour')
            this.shiftActTime()
            break
        }
      },
      checkToday () {
        if (this.type === 'min') {
          this.time = this.$moment().format('YYYY-MM-DD HH:mm')
        } else if (this.type === 'day') {
          this.time = this.$moment().format('YYYY-MM-DD')
        }
        this.$emit('change', this.time, this.dateSouce)
        this.close()
      },
      showYear () {
        let year = this.$moment(this.checked.currentMoment).year()
        this.library.year = []
        let yearTmp = []
        for (let i = year - 100; i < year + 5; ++i) {
          yearTmp.push(i)
        }
        this.library.year = yearTmp
        this.showOne('year')
        this.$nextTick(() => {
          let listDom = document.getElementById('yearList')
          listDom.scrollTop = (listDom.scrollHeight - 100)
          this.addYear()
        })
      },
      showOne (type) {
        switch (type) {
          case 'year':
            this.showInfo.hour = false
            this.showInfo.day = false
            this.showInfo.year = true
            this.showInfo.month = false
            break
          case 'month':
            this.showInfo.hour = false
            this.showInfo.day = false
            this.showInfo.year = false
            this.showInfo.month = true
            break
          case 'day':
            this.showInfo.hour = false
            this.showInfo.day = true
            this.showInfo.year = false
            this.showInfo.month = false
            break
          case 'hour':
            this.showInfo.hour = true
            this.showInfo.day = false
            this.showInfo.year = false
            this.showInfo.month = false
            break
          default:
            this.showInfo.day = true
            this.showInfo.year = false
            this.showInfo.month = false
            this.showInfo.hour = false
        }
      },
      showMonth () {
        this.showOne('month')
      },
      addYear () {
        let listDom = document.getElementById('yearList')
        listDom.addEventListener('scroll', (e) => {
          if (listDom.scrollTop < listDom.scrollHeight - 100) {
            let len = this.library.year.length
            let lastYear = this.library.year[len - 1]
            this.library.year.push(lastYear + 1)
          }
        }, false)
      },
      setYear (year) {
        this.checked.currentMoment = this.$moment(year + '-' + this.checked.month + '-' + this.checked.day)
        if (this.type === 'year') {
          this.checked.year = `${year}`
          this.showOne('month')
        } else {
          this.showDay(this.checked.currentMoment)
        }
      },
      setMonth (month) {
        let mo = (this.library.month.indexOf(month) + 1)
        if (mo < 10) {
          mo = '0' + '' + mo
        }
        this.checked.currentMoment = this.$moment(this.checked.year + '-' + mo + '-' + this.checked.day)
        if (this.type === 'year') {
          this.time = `${this.checked.year}-${month}`
          this.showInfo.check = false
          this.$emit('change', this.time, this.dateSouce)
        } else {
          this.showDay(this.checked.currentMoment)
        }
      },
      remove () {
        this.time = undefined
        this.showInfo.check = false
        this.$emit('change', this.time, this.dateSouce)
      },
      showCheck () {
        if (this.disabled) {
          if (this.disabledTip) {
            this.$message.warning(this.disabledTip)
          }
          return
        }
        if (this.time === '') {
          this.showDay()
        } else {
          if (this.type === 'day' || this.type === 'min') {
            this.checked.oldtime = this.time
            this.showDay(this.time)
          } else {
            if (this.selectedDays.length) {
              this.checked.oldtime = this.selectedDays[0]
              this.showDay(this.selectedDays[0])
            } else {
              this.showDay()
            }
          }
        }
        if (this.type === 'year') {
          this.showOne('month')
        }
        this.showInfo.check = true
      },
      setTime (type, obj, list) {
        for (let item of list) {
          item.checked = false
          if (item.value === obj.value) {
            item.checked = true
            this.checked[type] = item.value
          }
        }
      },
      picked () {
        if (this.type === 'min') {
          let ctime = `${this.checked.year}-${this.checked.month}-${this.checked.day}-${this.checked.hour}:${this.checked.min}`
          this.checked.currentMoment = this.$moment(ctime, 'YYYY-MM-DD HH:mm')
          this.time = this.$moment(this.checked.currentMoment).format('YYYY-MM-DD HH:mm')
        } else if (this.type === 'hour') {
          let ctime = `${this.checked.year}-${this.checked.month}-${this.checked.day}-${this.checked.hour}`
          this.checked.currentMoment = this.$moment(ctime, 'YYYY-MM-DD HH')
          this.time = this.$moment(this.checked.currentMoment).format('YYYY-MM-DD HH')
        } else if (this.type === 'day') {
          let ctime = this.checked.year + '-' + this.checked.month + '-' + this.checked.day
          this.checked.currentMoment = this.$moment(ctime, 'YYYY-MM-DD')
          this.time = this.$moment(this.checked.currentMoment).format('YYYY-MM-DD')
        } else {
          this.time = JSON.stringify(this.selectedDays)
        }
        this.showInfo.check = false
        this.$emit('change', this.time, this.dateSouce)
      },
      doThisYear () {
        this.time = `${this.checked.year}`
        this.showInfo.check = false
        this.$emit('change', this.time, this.dateSouce)
      },
      dismiss (evt) {
        if (evt.target.className === 'datepicker-overlay') {
          if (this.option.dismissible === undefined || this.option.dismissible) {
            this.close()
          }
        }
      },
      showClear () {
        if (this.disabled) {
          return
        }
        this.isClear = true
      },
      hideClear () {
        this.isClear = false
      },
      clear () {
        this.time = ''
        this.defaultplace = ''
        this.$emit('change', this.time, this.dateSouce)
        this.close()
      },
      close () {
        this.showInfo.check = false
        this.$emit('cancel')
      },
      shiftActTime () {
        // shift activated time items to visible position.
        this.$nextTick(() => {
          if (!document.querySelector('.hour-item.active')) {
            return false
          }
          document.querySelector('.hour-box').scrollTop = (document.querySelector('.hour-item.active').offsetTop || 0) - 250
          document.querySelector('.min-box').scrollTop = (document.querySelector('.min-item.active').offsetTop || 0) - 250
        })
      }
    }
  }
</script>
<style lang="scss" scoped>
  .date-input{
    @include input-l(115px)
  }
  .date-full{
    @include input-l(100%);
    line-height: 1.15;
  }
  .date-input-noborder {
    @include input-l(115px);
    border: 0;
    &:focus{
      border: 0;
    }
  }
  .datepicker-overlay {
    position: fixed;
    width: 100%;
    height: 100%;
    z-index: 11;
    top: 0;
    left: 0;
    overflow: hidden;
    animation: fadein 0.5s;
  }

  @keyframes fadein {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }

  .cov-date-body {
    background: #fff;
    overflow: hidden;
    font-size: 14px;
    display: block;
    width: 310px;
    .cov-date-head{
      height: 15px;
      line-height: 15px;
      padding: 15px 20px;
      font-size: $font-size-base;
      border-bottom: 1px solid $color-line;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      margin-bottom: 15px;
      .icon-x{
        font-size: 24px;
        cursor: pointer;
        color: $color-grid;
        &:hover{
          color: $red;
        }
      }
    }
    .cov-date-foot{
      border-top: 1px $color-line solid;
      padding: 8px 20px 8px 15px;
      background: $color-bg;
    }
  }
  .cov-date-body-fixed{
    position: fixed;
    z-index: 11;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2);
  }
  .cov-picker-box {
    background: #fff;
    display: inline-block;
    box-sizing: border-box !important;
    height: 220px;
    text-align: start!important;
  }

  .icon-talk-x{
    position: absolute;
    color: #fff;
    cursor: pointer;
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: $red;
    line-height: 13px;
    text-align: center;
    i{
      font-size: $font-size-smallest;
    }
  }

  .day {
    width: 38px;
    height: 30px;
    line-height: 30px;
    display: inline-block;
    text-align: center;
    cursor: pointer;
    padding: 0;
    color: #677370;
    background: #fff;
    margin-bottom: 5px;
    .passive-day{
      color: #999;
    }
    .unavailable{
      color: #999;
      cursor: not-allowed;
    }
    >p{
      width: 30px;
      height: 30px;
      border-radius: 50%;
      margin: 0 auto;
      &:hover{
        background: #edfaf7;
        color: $color;
      }
    }
  }

  .checked{
    color: $color;
  }

  .week ul {
    margin: 0 0 8px 9px;
    padding: 0;
    list-style: none;
    :last-child{
      width: 20px;
    }
  }

  .week ul li {
    width: 39px;
    display: inline-block;
    text-align: left;
    background: transparent;
    color: #677370;
    font-weight: bold;
    font-size: 0.9rem;
  }

  .cov-date-monthly {
    margin-bottom: 20px;
    > div {
      float: left;
      text-align: center;
      cursor: pointer;
    }
  }

  .cov-date-previous,
  .cov-date-next {
    vertical-align: middle;
    width: 20% !important;
  }

  .cov-date-caption {
    width: 160px;
    box-sizing: border-box;
    font-size: 16px;
  }

  .cov-date-caption b:hover {
    color: $color;
  }

  .cov-date-previous:hover,
  .cov-date-next:hover {
    color: $color;
  }

  .date-item {
    text-align: center;
    font-size: 16px;
    padding: 5px 0;
    cursor: pointer;
    width: 270px;
  }

  .date-item:hover {
    background: #e0e0e0;
  }

  .date-list {
    overflow-y: auto;
    overflow-x: hidden;
    vertical-align: top;
    padding: 0;
  }

  .cov-vue-date {
    display: inline-block;
    vertical-align: top;

    .icon-date{
      position: absolute;
      right: 10px;
      top: 7px;
    }
  }

  .button-box {
    background: #fff;
    vertical-align: top;
    line-height: 30px;
    text-align: right;
    margin-top: 20px;
    .ok{
      @include button-b($color, #fff, 3px);
      width: 70px;
      margin-left: 7px;
    }
    .cancel{
      @include button-b(#afbcc2, #fff, 3px);
      width: 70px;
    }
  }

  .watch-box {
    height: 100%;
    overflow: hidden;
    margin-left: 80px;
  }

  .hour-box,
  .min-box {
    display: inline-block;
    width: 50%;
    text-align: center;
    height: 100%;
    overflow: auto;
    float: left;
  }

  .date-width{
    width: 100%;
  }

  .hour-box ul,
  .min-box ul {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  .hour-item,
  .min-item {
    padding: 10px 17px;
    font-size: 24px;
    cursor: pointer;
  }

  .hour-item:hover,
  .min-item:hover {
    background: #E3E3E3;
  }
  .active{
    background: #E3E3E3;
  }
  ::-webkit-scrollbar {
    width: 2px;
  }

  ::-webkit-scrollbar-track {
    background: #E3E3E3;
  }

  ::-webkit-scrollbar-thumb {
    background: #C1C1C1;
    border-radius: 2px;
  }
</style>
